package com.bsoft.service.impl;

import com.bsoft.service.IRestHighLevelClientService;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.settings.put.UpdateSettingsRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.client.indices.PutMappingRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.index.reindex.BulkByScrollResponse;
import org.elasticsearch.index.reindex.DeleteByQueryRequest;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.AvgAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
public class RestHighLeveClientServiceImpl implements IRestHighLevelClientService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Override
    public CreateIndexResponse createIndex() throws IOException {
        String indexName = "student";
        CreateIndexRequest request = new CreateIndexRequest(indexName.toLowerCase());
        request.settings(Settings.builder()
                .put("index.number_of_shards", 5)
                .put("index.number_of_replicas", 0)
        );
        // mapping部分，除了用json字符串来定义外，还可以使用Map或者XContentBuilder
        // 这里使用XContentBuilder
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            builder.startObject("properties");
            {
                builder.startObject("id");
                {
                    builder.field("type", "integer");
                }
                builder.endObject();
                builder.startObject("name");
                {
                    builder.field("type", "text");
                }
                builder.endObject();
                builder.startObject("age");
                {
                    builder.field("type", "integer");
                }
                builder.endObject();
                builder.startObject("description");
                {
                    builder.field("type", "text");
                    builder.field("analyzer", "ik_max_word");
                }
                builder.endObject();
                builder.startObject("birthday");
                {
                    builder.field("type", "date");
                }
                builder.endObject();
            }
            builder.endObject();
        }
        builder.endObject();
        request.mapping(builder.toString());
        // 同步的方式执行
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        // 异步的方式执行
        restHighLevelClient.indices().createAsync(request, RequestOptions.DEFAULT, new ActionListener<CreateIndexResponse>() {
            @Override
            public void onResponse(CreateIndexResponse createIndexResponse1) {
                System.out.println("执行情况:" + createIndexResponse1);
            }

            @Override
            public void onFailure(Exception e) {
                System.out.println("执行失败的原因:" + e.getMessage());
            }
        });
        return createIndexResponse;

    }

    //删除索引
    @Override
    public AcknowledgedResponse deleteIndex() throws IOException {
        String indexName = "student";
        DeleteIndexRequest indexRequest = new DeleteIndexRequest(indexName);
        //同步删除
        AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(indexRequest, RequestOptions.DEFAULT);

        return acknowledgedResponse;
    }

    //查询所有是否存在
    @Override
    public boolean existIndex() throws IOException {
        String indexName = "student";
        GetIndexRequest request = new GetIndexRequest(indexName);
        //同步执行
        boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
        return exists;
    }

    @Override
    public AcknowledgedResponse updateIndexSettings() throws IOException {
        String indexName = "student";
        UpdateSettingsRequest request = new UpdateSettingsRequest(indexName);
        String settingKey = "index.number_of_replicas";
        int settingValue = 2;
        Settings.Builder builder = Settings.builder().put(settingKey, settingValue);
        request.settings(builder);
        //是否更新setting配置(同步)
        AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().putSettings(request, RequestOptions.DEFAULT);

        return acknowledgedResponse;
    }
    //更新索引mapping配置

    @Override
    public AcknowledgedResponse updateIndexMapping() throws IOException {
        String indexName = "student";
        PutMappingRequest request = new PutMappingRequest(indexName);
        XContentBuilder builder = XContentFactory.jsonBuilder();
        builder.startObject();
        {
            builder.startObject("properties");
            //会在一起索引的基础上新增sex
            builder.startObject("sex");
            {
                builder.field("type", "integer");
            }
            builder.endObject();
        }
        builder.endObject();
        request.source(builder);
        //新增mappering 配置同步
        AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().putMapping(request, RequestOptions.DEFAULT);

        return acknowledgedResponse;
    }

    //新增文档
    @Override
    public IndexResponse addDocument() throws IOException {
        String indexName = "student";
        IndexRequest request = new IndexRequest(indexName);
        //id为1的数据
        request.id("1");
        Map<String, Object> jsonMap = new HashMap<>();
        jsonMap.put("id", 1);
        jsonMap.put("name", "tom");
        jsonMap.put("age", 24);
        jsonMap.put("description", "tom是一个好学生");
        jsonMap.put("brithday", new Date());
        jsonMap.put("sex", 1);
        request.source(jsonMap);
        request.routing("routing");

        //同步方式
        IndexResponse indexResponse = restHighLevelClient.index(request, RequestOptions.DEFAULT);

        return indexResponse;
    }

    //修改文档
    @Override
    public UpdateResponse updateDocument() throws IOException {
        String indexName = "student";
        //传入索引名称和需要更新的Document id
        UpdateRequest request = new UpdateRequest(indexName, "1");
        request.doc("description", "Tom是一个好学生 考上大学没问题");
        // 同步方式
        UpdateResponse update = restHighLevelClient.update(request, RequestOptions.DEFAULT);

        return update;
    }

    //根据id删除文档
    @Override
    public DeleteResponse deleteDocumentById() throws IOException {
        String indexName = "student";
        DeleteRequest deleteRequest = new DeleteRequest(indexName, "1");
        //同步方式

        DeleteResponse delete = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);

        return delete;
    }

    //根据条件删除文档
    @Override
    public BulkByScrollResponse deleteDocumentByCon() throws IOException {
        String indexName = "student";
        DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(indexName);
        //待删除的数据满足需要的条件
        deleteByQueryRequest.setQuery(new TermQueryBuilder("name", "tom"));
        //忽略版本冲突
        deleteByQueryRequest.setConflicts("proceed");
        BulkByScrollResponse bulkByScrollResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);

        return bulkByScrollResponse;
    }

    //批量操作文档
    @Override
    public BulkResponse bulkDocument() throws IOException {
        String indexName = "student";
        BulkRequest request = new BulkRequest();
        // 普通的PUT操作，相当于全量替换或新增
        request.add(new IndexRequest(indexName).id("2").source(XContentType.JSON, "name", "zsx", "age", "25"));
        // 更新操作
        request.add(new UpdateRequest(indexName, "2").doc(XContentType.JSON, "sex", 1));
        // 删除操作
        request.add(new DeleteRequest(indexName, "2"));
        // 同步操作
        BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);
        boolean hasFailures = bulkResponse.hasFailures();
        System.out.println("批量操作是否失败：" + hasFailures);
        BulkItemResponse[] items = bulkResponse.getItems();
        for (BulkItemResponse item : items) {
            System.out.println(item.status());
        }

        return bulkResponse;
    }

    @Override
    public SearchResponse searchDocument1() throws IOException {
        String indexName = "student";
        SearchRequest searchRequest = new SearchRequest(indexName);
        BoolQueryBuilder booleanQueryBuilder = QueryBuilders.boolQuery();
        // 过滤出年龄在15~40岁之间的document
        booleanQueryBuilder.filter(QueryBuilders.rangeQuery("age").from(15).to(40));
        // bool must条件， 找出description字段中包含学生的document
        booleanQueryBuilder.must(QueryBuilders.matchQuery("description", "学生"));
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 执行查询条件
        // sourceBuilder.query(QueryBuilders.matchAllQuery());
        sourceBuilder.query(booleanQueryBuilder);
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "tom");
        sourceBuilder.query(matchQueryBuilder);
        //聚合年龄分布
        TermsAggregationBuilder ageAgg = AggregationBuilders.terms("ageAgg").field("age");
        sourceBuilder.aggregation(ageAgg);
        //聚合平均年龄
        AvgAggregationBuilder balanceAvg = AggregationBuilders.avg("ageAvg").field("age");
        sourceBuilder.aggregation(balanceAvg);
        // 分页查询
        sourceBuilder.from(0);
        sourceBuilder.size(5);
        // 排序
        sourceBuilder.sort("age", SortOrder.DESC);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
        searchRequest.source(sourceBuilder);
        // 同步的方式发送请求
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            String hitString = hit.getSourceAsString();
            System.out.println(hitString);
        }

        return searchResponse;
    }

    @Override
    public SearchResponse searchDocument2() throws IOException {
        String indexName = "student";
        SearchRequest searchRequest = new SearchRequest(indexName);
        //构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                // 在student索引的description和name字段中都查询“tom”
                .query(QueryBuilders.multiMatchQuery("tom", "description", "name"))
                // matchQuery是模糊查询，会对key进行分词
                // searchSourceBuilder.query(QueryBuilders.matchQuery(key,value));
                // termQuery是精准查询
                // searchSourceBuilder.query(QueryBuilders.termQuery(key,value));
                .sort(SortBuilders.fieldSort("age").order(SortOrder.DESC))
                // 一个可选项，用于控制允许搜索的时间
                // searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
                // 指定从哪条开始查询
                .from(0)
                // 需要查出的总记录条数
                .size(10);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            String hitString = hit.getSourceAsString();
            System.out.println(hitString);
        }
        // 异步方式发送请求
        /*
        restHighLevelClient.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener<SearchResponse>() {

            @Override
            public void onResponse(SearchResponse searchResponse) {
                System.out.println("执行情况: " + searchResponse);

            }

            @Override
            public void onFailure(Exception e) {
                System.out.println("执行失败的原因:" + e.getMessage());
            }
        });
        */
        return searchResponse;

    }

    @Override
    public SearchResponse searchDocument3() throws IOException {
        String indexName = "student";
        SearchRequest searchRequest = new SearchRequest(indexName);
        // 高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");
        highlightBuilder.field("description");
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
                .query(QueryBuilders.multiMatchQuery("tom", "description", "name"))
                .sort(SortBuilders.fieldSort("age").order(SortOrder.DESC))
                // 指定从哪条开始查询
                .from(0)
                // 需要查出的总记录条数
                .size(10)
                //高亮
                .highlighter(highlightBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHit[] hits = searchResponse.getHits().getHits();
        for (SearchHit hit : hits) {
            String hitString = hit.getSourceAsString();
            System.out.println(hitString);
            // 处理高亮显示的结果
            HighlightField titleField = hit.getHighlightFields().get("name");
            if (titleField != null) {
                // 新建一个对象，把该属性的值重新覆盖
                System.out.println(titleField.getFragments()[0].toString());
            }
            HighlightField contentField = hit.getHighlightFields().get("description");
            if (contentField != null) {
                System.out.println(contentField.getFragments()[0].toString());
            }
        }
        // 异步方式发送请求
        /*
        restHighLevelClient.searchAsync(searchRequest, RequestOptions.DEFAULT, new ActionListener<SearchResponse>() {

            @Override
            public void onResponse(SearchResponse searchResponse) {
                System.out.println("执行情况: " + searchResponse);

            }

            @Override
            public void onFailure(Exception e) {
                System.out.println("执行失败的原因:" + e.getMessage());
            }
        });
        */
        return searchResponse;

    }
}
